﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WordNet.Core.ElementContracts;

namespace WordNet.Core
{
    public class IndexWord: IIndexWord
    {
        private readonly IIndexWordID _id;
        private readonly int _tagSenseCount;
        private readonly HashSet<IPointer> _pointers;
        private readonly List<IWordID> _wordIDs;
       
        public IndexWord(String lemma, POS pos, int tagSenseCnt, params IWordID[] words)
            : this(new IndexWordID(lemma, pos), tagSenseCnt, null, words)
        { }

        public IndexWord(String lemma, POS pos, int tagSenseCnt, IPointer[] ptrs, params IWordID[] words)
            : this(new IndexWordID(lemma, pos), tagSenseCnt, ptrs, words)
        { }

        public IndexWord(IIndexWordID id, int tagSenseCnt, params IWordID[] words)
            : this(id, tagSenseCnt, null, words)
        { }

        public IndexWord(IIndexWordID id, int tagSenseCnt, IPointer[] ptrs, params IWordID[] words)
        {
            if (id == null)
                throw new ArgumentNullException();
            if (tagSenseCnt < 0)
                throw new ArgumentException();
            if (words.Length == 0)
                throw new ArgumentException();
            foreach (IWordID wid in words)
                if (wid == null)
                    throw new ArgumentNullException();

            // do pointers as of v2.3.0
            HashSet<IPointer> pointers;
            if (ptrs == null || ptrs.Length == 0)
            {
                pointers = new HashSet<IPointer>();
            }
            else
            {
                pointers = new HashSet<IPointer>();

                foreach (IPointer p in ptrs)
                    if (p == null)
                    {
                        throw new ArgumentNullException();
                    }
                    else
                    {
                        pointers.Add(p);
                    }
            }

            this._id = id;
            this._tagSenseCount = tagSenseCnt;
            this._wordIDs = new List<IWordID>(words);
            this._pointers = pointers;
        }
        
        public string Lemma
        {
            get { return _id.Lemma; }
            
        }

        public List<IWordID> GetWordIDs()
        {
            return _wordIDs.ToList();
        }

        public int TagSenseCount { get { return _tagSenseCount; } }

        public HashSet<IPointer> GetPointers()
        {
            return new HashSet<IPointer>(_pointers);
        }

        public POS PartOfSpeech
        {
            get { return _id.PartOfSpeech; }
        }

        public IIndexWordID ID
        {
            get { return _id; }
        }

        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append('[');
            sb.Append(_id.Lemma);
            sb.Append(" (");
            sb.Append(_id.PartOfSpeech);
            sb.Append(") ");
            for (IEnumerator<IWordID> i = _wordIDs.GetEnumerator(); i.MoveNext(); )
            {
                sb.Append(i.Current.ToString());
                if (i.MoveNext())
                    sb.Append(", ");
            }
            sb.Append(']');
            return sb.ToString();
        }

        public override int GetHashCode()
        {
            const int prime = 31;
            int result = 1;
            result = prime * result + _id.GetHashCode();
            result = prime * result + _tagSenseCount;
            foreach(var w in _wordIDs)
                result = prime * result + w.GetHashCode();
            foreach(var p in _pointers)
                result = prime * result + p.GetHashCode();
            return result;
        }

        public override bool Equals(Object obj)
        {
            if (Object.ReferenceEquals(obj, null))
                return false;
            if (Object.ReferenceEquals(this, obj))
                return true;

            IIndexWord other = obj as IIndexWord;
            if (other == null)
                return false;
            
            if (!_id.Equals(other.ID))
                return false;
            if (_tagSenseCount != other.TagSenseCount)
                return false;
            if (!_wordIDs.SequenceEqual(other.GetWordIDs()))
                return false;
            if (!_pointers.SequenceEqual(other.GetPointers()))
                return false;
            return true;
        }
        
    }
}
